package com.yc.mvc.web;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.sql.Timestamp;
import java.util.*;

/**
 * Company 源辰信息
 *
 * @author navy
 * @date 2024/5/4
 * Email haijunzhou@hnit.edu.cn
 */
public class MyDispatchServlet extends HttpServlet {

    // 控制器集合 ==> IOC 容器
    Set<Object> controllerSet = new HashSet<>();
    {
        controllerSet.add(new DemoDo());
        controllerSet.add(new IndexController());
        controllerSet.add(new HelloAction());
    }

    LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer
            = new LocalVariableTableParameterNameDiscoverer();


    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 分析请求地址
        String path = req.getServletPath();
        System.out.println("path = " + path);

        // 查找控制器 =>对象 + 方法
        Object controller = null;
        Method method = null;
        for (Object object : controllerSet) {
            RequestMapping requestMapping = object.getClass().getAnnotation(RequestMapping.class);
            String pathPrefix = "";
            if(requestMapping!=null){
                pathPrefix = requestMapping.value().length>0 ?
                        requestMapping.value()[0] : requestMapping.path()[0];
                if(pathPrefix.startsWith("/") == false){
                    pathPrefix = "/" + pathPrefix;
                }
            }

            for (Method declaredMethod : object.getClass().getDeclaredMethods()) {
                RequestMapping requestMapping1 = declaredMethod.getAnnotation(RequestMapping.class);
                String methodPath = "";
                if(requestMapping1 != null){
                    methodPath = requestMapping1.value().length>0 ?
                            requestMapping1.value()[0] : requestMapping1.path()[0];
                    String fullPath = pathPrefix + "/" + methodPath;
                    // 精确的地址匹配, 没有实现 ? * {} 模糊匹配
                    if(fullPath.equals(path)){
                        controller = object;
                        method = declaredMethod;
                    }
                }
            }
        }
        if(controller == null){
            // 1. 静态资源
            // 2. 404
            System.out.println("目标资源不存在:" + path);
            return;
        } else {
            System.out.println("controller = " + controller);
            System.out.println("method = " + method);
        }

        // 执行控制器
        Object ret = null;
        if (method.getParameterCount()==0) {
            try {
                ret = method.invoke(controller);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } else {
            // 获取方法参数名
            String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
            // 参数值数组
            Object[] parameterValues = new Object[parameterNames.length];
            System.out.println("parameterNames: " + Arrays.toString(parameterNames));
            for (int i = 0; i < parameterNames.length; i++) {
                String parameterName = parameterNames[i];
                String svalue = req.getParameter(parameterName);
                // 参数反射对象
                Parameter parameter = method.getParameters()[i];
                Class<?> type = parameter.getType();
                RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
                if(svalue==null){
                    // 判定为 实体对象类型, 例如: type = Person.class
                    if( !(type.isPrimitive() || type.equals(String.class ))){
                        // 实体类型 or Servlet对象
                        if (parameter.getAnnotation(RequestBody.class)!=null) {
                            // json
                            ObjectMapper objectMapper = new ObjectMapper();
                            String body = null;
                            // 获取请求体内容
                            ServletInputStream in = req.getInputStream();
                            byte[] bytes = new byte[1024];
                            int count = in.read(bytes);
                            body = new String(bytes,0,count);
                            // json 字符串 转 对象
                            Object object = objectMapper.readValue(body, type);
                            parameterValues[i] = object;
                            continue;
                        } else {
                            // 表单参数
                            //  循环 请求参数， 对 实体对象属性赋值
                            try {
                                Object object = type.newInstance();
                                Enumeration<String> names = req.getParameterNames();
                                while(names.hasMoreElements()){
                                    // 参数名
                                    String name = names.nextElement();
                                    // 参数值
                                    String sValue = req.getParameter(name);
                                    // 递归调用，设置参数
                                    setValue(object, name, sValue,req);
                                }
                                System.out.println("object = " + object);
                                parameterValues[i] = object;
                            } catch (Exception e) {
                                throw new RuntimeException(e);
                            }
                            continue;
                        }
                    }

                    if(requestParam==null || requestParam.required() == true){
                        // 判断是否是基本数据类型
                        if (type.isPrimitive()) {
                            // 基本数据类型, 不允许空值
                            resp.sendError(400,"not parameter value: " + parameterName);
                            return;
                        } else {
                            // 非基本数据类型, 允许空值
                            parameterValues[i] = null;
                            continue;
                        }
                    } else {
                        // 获取 requestParam 设置默认值
                        svalue = requestParam.defaultValue();
                    }
                }
                // svalue != null
                parameterValues[i] = castStringValue(svalue, type);
            }
            try {
                // 强制访问
                method.setAccessible(true);
                ret = method.invoke(controller,parameterValues);
            } catch (Exception e) {
                throw new RuntimeException("控制器方法执行失败: " + method,e);
            }
        }

        // 返回结果
        RestController restControllerAnno = controller.getClass().getAnnotation(RestController.class);
        Controller controllerAnno = controller.getClass().getAnnotation(Controller.class);
        if(restControllerAnno!=null){
            if (ret instanceof ModelAndView) {
                // 页面跳转
                String redirectPath = ((ModelAndView)ret).getViewName();
                redirectPath = redirectPath.substring("redirect:".length());
                resp.sendRedirect(redirectPath);
            } else {
                // json   jackson
                ObjectMapper objectMapper = new ObjectMapper();
                String json = objectMapper.writeValueAsString(ret);
                resp.setContentType("application/json;charset=utf-8");
                resp.getWriter().append(json);
            }
        } else if(controllerAnno!=null){
            if(method.getAnnotation(ResponseBody.class) != null){
                // 返回json  TODO
            } else if (ret instanceof ModelAndView || ret instanceof String) {
                // 页面跳转   TODO
            }
        }

    }

    void setValue(Object object, String parameterName, String sValue, HttpServletRequest req) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        try {
            //  parameterName = father.father.father.name
            if (parameterName.contains(".")) {
                // 带 . 号
                String fieldName = parameterName.substring(0, parameterName.indexOf("."));
                Field field = object.getClass().getDeclaredField(fieldName);
                Object objectValue = field.getType().newInstance();
                field.setAccessible(true);
                field.set(object, objectValue);
                parameterName = parameterName.substring(parameterName.indexOf(".") + 1);
                setValue(objectValue, parameterName, sValue, req);
            } else {
                // 不带 . 号
                // 要设置的属性对象
                Field field = object.getClass().getDeclaredField(parameterName);
                if (field.getType().equals(Timestamp.class)) {
                    return;
                }
                Object value = null;
                if (field.getType().isArray()) {
                    // 数组型参数
                    String[] values = req.getParameterValues(parameterName);
                    value = values;
                } else if (field.getType().isAssignableFrom(Collection.class)) {
                    // 集合类型不处理
                    //            } else if(!(object.getClass().isPrimitive() || object.getClass().equals(String.class ))){
                    //                // 实体类

                } else {
                    // 转换成当前属性类型的值
                    value = castStringValue(sValue, field.getType());
                }
                field.setAccessible(true);
                field.set(object, value);
            }
        }catch(NoSuchFieldException e){
            e.printStackTrace();
        }
    }



    private Object castStringValue(String svalue, Class<?> type) {
        // int or Integer
        if(type.equals(Integer.class) || type.equals(int.class)){
            return Integer.valueOf(svalue);
        } else if (type.equals(Long.class) || type.equals(long.class)){
            return Long.valueOf(svalue);
//        } else if( 其他类型 ){  TODO
        } else if(type.equals(Boolean.class) || type.equals(boolean.class)){
            return Boolean.valueOf(svalue);
        } else if(type.equals(String.class)){
            return svalue;
        } else {
            return svalue;
        }
    }
}
